/**
 * Created on May 13, 2007
 */
package jvs.vfs.proc;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import jvs.vfs.FileSystem;
import jvs.vfs.Fstab;
import jvs.vfs.IFileBaseImpl;

/**
 * @author qiangli
 * 
 */
public class ProcFileImpl extends IFileBaseImpl {

	private static final String[] filelist = {"/contenttypes", "/cpuinfo", "/filesystems", "/meminfo", "/mounts",
			"/properties", "/threads" , "/uptime", "/version"};

	private ThreadGroup tg = null;
	private Thread th = null;
	
	/**
	 * @param uri
	 */
	public ProcFileImpl(URI uri) {
		super(uri);
		//
		String p = uri.getPath();
		if(p.startsWith("/threads/")) {
			ThreadGroup top = findTopThreadGroup();
			String name = this.getName();
			tg = findThreadGroup(p,top);
			if (tg == null) {
				tg = findThreadGroup(this.getParent(),top);
			}
			if (tg != null && !tg.getName().equals(name)) {
				th = findThread(name,tg);
			}
		}
	}

	public boolean create() {
		return false;
	}

	public boolean delete() {
		return false;
	}

	@Override
	public boolean canRead() {
		String p = uri.getPath();
		for (int i = 0; i < filelist.length; i++) {
			if (filelist[i].equals(p)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean canWrite() {
		return false;
	}

	public boolean exists() {
		String p = uri.getPath();
		if (p.equals("/")) {
			return true;
		}
		for (int i = 0; i < filelist.length; i++) {
			if (filelist[i].equals(p)) {
				return true;
			}
		}
		// threads
		if (p.startsWith("/threads/")) {
			return tg != null;
		}

		return false;
	}

	public InputStream getInputStream() {
		String c = getContent();
		if (c == null) {
			return null;
		}
		return new ByteArrayInputStream(c.getBytes());
	}

	private String format(List<String> al) {
		Collections.sort(al);
		StringBuffer sb = new StringBuffer();
		for (Iterator<String> it = al.iterator(); it.hasNext();) {
			sb.append(it.next() + "\n");
		}
		return sb.toString();
	}

	public OutputStream getOutputStream() {
		return null;
	}

	public boolean isDirectory() {
		String p = uri.getPath();
		if (p.equals("/")) {
			return true;
		}
		//threads
		if (p.equals("/threads")) {
			return true;
		}
		
		if (tg != null && th == null) {
			return true;
		}
		
		return false;
	}

	public String[] list() {
		String p = uri.getPath();
		if (p.equals("/")) {
			return filelist;
		}
		if (p.equals("/threads")) {
			ThreadGroup tg = findTopThreadGroup();
			return new String[] {tg.getName()};
		}
		if (p.startsWith("/threads/")) {
			if (tg != null) {
				List<String> list = new ArrayList<String>();
				ThreadGroup[] tga = new ThreadGroup[tg.activeGroupCount()];
				tg.enumerate(tga,false);
				for (int i = 0; i < tga.length; i++) {
					if (tga[i] == null) {
						continue;
					}
					list.add(tga[i].getName());
				}
				Thread[] ta = new Thread[tg.activeCount()];
				tg.enumerate(ta,false);
				for (int i = 0; i < ta.length; i++) {
					if(ta[i] == null) {
						continue;
					}	
					list.add(ta[i].getName());
				}
				return (String[])list.toArray(new String[0]);
			}
		}
		return null;
	}

	private ThreadGroup findTopThreadGroup() {
		ThreadGroup top = Thread.currentThread().getThreadGroup();
		while (true) {
			if (top.getParent() != null) {
				top = top.getParent();
			} else {
				break;
			}
		}
		return top;
	}

	private ThreadGroup findThreadGroup(String path,ThreadGroup top) {
		String p = buildPath(top);
		if (p.equals(path)) {
			return top; 
		}
		ThreadGroup[] ta = new ThreadGroup[top.activeGroupCount()];
		top.enumerate(ta,true);
		for (int i = 0; i < ta.length; i++) {
			p = buildPath(ta[i]);
			if (p.equals(path)) {
				return ta[i]; 
			}
		}
		return null;
	}


	private String buildPath(ThreadGroup group) {
		StringBuffer sb = new StringBuffer("/"+group.getName());
		while (true) {
			if (group.getParent() != null) {
				group = group.getParent();
				sb.insert(0,"/"+group.getName());
			} else {
				break;
			}
		}

		return "/threads"+sb.toString();
	}
	
	private Thread findThread(String name,ThreadGroup tg) {
		Thread[] ta = new Thread[tg.activeCount()];
		tg.enumerate(ta,false);
		for (int i = 0; i < ta.length; i++) {
			if (ta[i] == null) {
				continue;
			}
			if (name.equals(ta[i].getName())) {
				return ta[i]; 
			}
		}
		return null;
	}

	public boolean mkdir() {
		return false;
	}

	public boolean move(URI uri) {
		return false;
	}

	public long getLength() {
		String c = getContent();
		if (c == null) {
			return -1;
		}
		return c.length();
	}

	@Override
	public String getContentType() {
		return "text/plain"; //TODO if directory
	}

	public String getContent() {
		String p = uri.getPath();
		FileSystem fs = FileSystem.getFileSystem();
		if (p.equals("/filesystems")) {
			Properties map = fs.getFsmap();
			List<String> al = new ArrayList<String>();
			for (Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator(); it.hasNext();) {
				Map.Entry<Object,Object> me = (Map.Entry<Object,Object>) it.next();
				al.add(me.getKey().toString() + " " + me.getValue().toString());
			}
			return format(al);
		} else if (p.equals("/mounts")) {
			Map<String, Fstab.Entry> map = fs.getMountpoints();
			List<String> al = new ArrayList<String>();
			for (Iterator<Map.Entry<String, Fstab.Entry>> it = map.entrySet().iterator(); it.hasNext();) {
				Map.Entry<String, Fstab.Entry> me = (Map.Entry<String, Fstab.Entry>) it.next();
				al.add(me.getValue().toString());
			}
			return format(al);
		} else if (p.equals("/properties")) {
			Properties map = x.java.lang.System.getProperties();
			List<String> al = new ArrayList<String>();
			for (Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator(); it.hasNext();) {
				Map.Entry<Object, Object> me = (Map.Entry<Object, Object>) it.next();
				al.add(me.getKey().toString() + "="
						+ me.getValue().toString());
			}
			return format(al);
		} else if (p.equals("/meminfo")) {
			java.lang.Runtime rt = java.lang.Runtime.getRuntime();
			StringBuffer sb = new StringBuffer();
			sb.append("Max Mem: " + rt.maxMemory() + "\n");
			sb.append("Total Mem: " + rt.totalMemory() + "\n");
			sb.append("Free Mem: " + rt.freeMemory() + "\n");
			return sb.toString();
		} else if (p.equals("/cpuinfo")) {
			return 
					("Available Processors: " + java.lang.Runtime.getRuntime()
							.availableProcessors());
		} else if (p.equals("/uptime")) {
			long ut = fs.getUptime();
			//long now = java.lang.System.currentTimeMillis();
			return ("System is up since: " + new Date(
					ut));
		} else if (p.equals("/version")) {
			return fs.getVersion();
		} else if (p.equals("/contenttypes")) {
			Properties map = fs.getContentTypes();
			List<String> al = new ArrayList<String>();
			for (Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator(); it.hasNext();) {
				Map.Entry<Object, Object> me = (Map.Entry<Object, Object>) it.next();
				al.add(me.getKey().toString() + "="
						+ me.getValue().toString());
			}
			return format(al);
		} 
		
		//threads
		
		if (th != null) {
			StringBuffer sb = new StringBuffer();
			sb.append("Name: "+th.getName()+"\n");
			sb.append("Group: "+th.getThreadGroup().getName()+"\n");
			sb.append("Priority: "+th.getPriority()+"\n");
			sb.append("Alive: "+th.isAlive()+"\n");
			sb.append("Daemon: "+th.isDaemon()+"\n");
			sb.append("Interrupted: "+th.isInterrupted()+"\n");
			
			return sb.toString();
		}
		return null;
	}
}
